package org.yaac.client.ui;

import static com.google.common.base.Strings.isNullOrEmpty;
import static com.google.common.collect.Lists.transform;

import java.util.ArrayList;
import java.util.List;

import javax.inject.Inject;

import org.yaac.client.service.CRUDServiceAsync;
import org.yaac.client.ui.EditorPropertyManager.PropertyEditCallback;
import org.yaac.client.util.AutoBeanUtil;
import org.yaac.client.util.ExceptionManager;
import org.yaac.client.widget.BlockingAsyncCallback;
import org.yaac.shared.file.CacheUploadResult;
import org.yaac.shared.file.CacheUploadResult.Status;
import org.yaac.shared.file.FileDownloadPath;
import org.yaac.shared.property.PropertyInfo;
import org.yaac.shared.property.PropertyInfo.FilePlaceHolder;

import com.google.common.base.Function;

import com.google.gwt.cell.client.ActionCell;
import com.google.gwt.cell.client.SafeHtmlCell;
import com.google.gwt.cell.client.TextCell;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.user.cellview.client.CellTable;
import com.google.gwt.user.cellview.client.Column;
import com.google.gwt.user.cellview.client.HasKeyboardSelectionPolicy.KeyboardSelectionPolicy;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FileUpload;
import com.google.gwt.user.client.ui.FormPanel;
import com.google.gwt.user.client.ui.FormPanel.SubmitCompleteEvent;
import com.google.gwt.user.client.ui.FormPanel.SubmitEvent;
import com.google.gwt.user.client.ui.TextArea;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.view.client.ListDataProvider;

/**
 * @author Max Zhu (thebbsky@gmail.com)
 *
 */
public class EditorPropertyPanel extends Composite {

	private static EditorPropertyPanelUiBinder uiBinder = GWT.create(EditorPropertyPanelUiBinder.class);

	interface EditorPropertyPanelUiBinder extends UiBinder<Widget, EditorPropertyPanel> {
	}

	static interface CloseBtnHandler {
		void onCloseBtnClick();
	}
	
	@UiField
	TextArea expTxt;
	
	// =======================file upload related========================	
	@UiField(provided=true)
	CellTable<FileDownloadPath> uploadTable; 
	
	private ListDataProvider<FileDownloadPath> dataProvider;
	
	@UiField
	FormPanel form;
	
	@UiField
	FileUpload upload;
	
	// ========================control panel =========================
	@UiField
	Button saveBtn;
	
	@UiField
	Button closeBtn;
	
	private CloseBtnHandler closeHandler;
	
	private String currKeyString;
	
	/**
	 * current edit callback
	 */
	private PropertyEditCallback callback;
	
	private final CRUDServiceAsync service;
	
	private final ExceptionManager exceptionMgr;
	
	@Inject
	EditorPropertyPanel(CRUDServiceAsync service, ExceptionManager exceptionMgr) {
		this.service = service;
		this.exceptionMgr = exceptionMgr;
		
		// init upload table
		uploadTable = new CellTable<FileDownloadPath>();
		uploadTable.setKeyboardSelectionPolicy(KeyboardSelectionPolicy.ENABLED);
		
		uploadTable.addColumn(new Column<FileDownloadPath, String>(new TextCell()) {
			@Override
			public String getValue(FileDownloadPath object) {
				// 0 --> 1
				// ...
				// n --> n+1
				return String.valueOf(dataProvider.getList().indexOf(object) + 1);
			}
		}, "Param Name");
		
		uploadTable.addColumn(new Column<FileDownloadPath, String>(new TextCell()) {
			@Override
			public String getValue(FileDownloadPath object) {
				return object.getFileName();
			}
		}, "File Name");
		
		uploadTable.addColumn(new Column<FileDownloadPath, String>(new TextCell()) {
			@Override
			public String getValue(FileDownloadPath object) {
				return object.getSize().toString();
			}
		}, "Size");
		
		// download
		uploadTable.addColumn(new Column<FileDownloadPath, SafeHtml>(new SafeHtmlCell()) {
			@Override
			public SafeHtml getValue(FileDownloadPath file) {
				String pathStr = AutoBeanUtil.encode(FileDownloadPath.class, file);
				
				SafeHtmlBuilder sb = new SafeHtmlBuilder();
				sb.appendHtmlConstant("<a href='/download?path=" + pathStr + "' target='blank'>Click to download</a>");
				return sb.toSafeHtml();
			}
		});
		
		// delete
		{
			ActionCell.Delegate<FileDownloadPath> delegate = new ActionCell.Delegate<FileDownloadPath>() {
				@Override
				public void execute(FileDownloadPath object) {
					dataProvider.getList().remove(object);
					dataProvider.refresh();
				}
			};
			
			uploadTable.addColumn(new Column<FileDownloadPath, FileDownloadPath>(
					new ActionCell<FileDownloadPath>("Delete", delegate)) {
				@Override
				public FileDownloadPath getValue(FileDownloadPath object) {
					return object;
				}
			});
		}
		
		// data provider
		dataProvider = new ListDataProvider<FileDownloadPath>();
		dataProvider.addDataDisplay(uploadTable);
		
		initWidget(uiBinder.createAndBindUi(this));
	}
	
	public void setCloseHandler(CloseBtnHandler closeHandler) {
		this.closeHandler = closeHandler;
	}

	/**
	 * @param event
	 */
	@UiHandler("form")
	void onFormSubmit(SubmitEvent event) {
		if (validateFileUpload()) {
			BlockingAsyncCallback.block("Uploading");	
		} else {
			event.cancel();
		}
	}
	
	private boolean validateFileUpload() {		
		if (isNullOrEmpty(upload.getFilename().trim())) {
			Window.alert("You must select a file to upload");	// TODO : i18n 
			return false;
		}
		return true;
	}
	
	@UiHandler("form")
	void onFormSubmitComplete(SubmitCompleteEvent event) {		
		BlockingAsyncCallback.unblock();
		
		// if event.getResults returns null, then the form is not even submitted
		if (!isNullOrEmpty(event.getResults())) {	// print out result
			CacheUploadResult result = AutoBeanUtil.decode(CacheUploadResult.class, event.getResults());
			
			if (result.getStatus() == Status.SUCCESS) {
				this.dataProvider.getList().add(result.getPath());
				this.dataProvider.refresh();
				this.form.reset();
			} else {	// upload failed
				Window.alert("Can't upload file");	// TODO : i18n
			}
		}
	}
	
	@UiHandler("saveBtn")
	void onSaveBtnClick(ClickEvent event) {
		String expression = expTxt.getText();
		
		List<String> filePaths = transform(this.dataProvider.getList(), new Function<FileDownloadPath, String>(){
			@Override
			public String apply(FileDownloadPath input) {
				return AutoBeanUtil.encode(FileDownloadPath.class, input);
			}
		});
		
		service.parsePropertyExp(currKeyString, expression, new ArrayList<String>(filePaths), 
				new BlockingAsyncCallback<PropertyInfo>() {
			@Override
			public void onBlockingCallSuccess(PropertyInfo result) {
				callback.onEditSuccess(result);
				quitEdit();
			}

			@Override
			public void onBlockingCallFailure(Throwable caught) {
				exceptionMgr.handle(caught);
			}
		});
	}
	
	@UiHandler("closeBtn")
	void onCloseBtnClick(ClickEvent event) {
		quitEdit();
	}

	private void quitEdit() {
		this.closeHandler.onCloseBtnClick();
	}
	
	/**
	 * apply default editor and start edit
	 * 
	 * @param info
	 */
	public void edit(String keyString, PropertyInfo info, final PropertyEditCallback callback) {
		this.callback = callback;
		this.currKeyString = keyString;
		
		// init status
		this.expTxt.setText(info.asExpression(new FilePlaceHolder()));
		
		List<String> paths = info.downloadPaths();
		List<FileDownloadPath> fileItems = transform(paths, new Function<String, FileDownloadPath>(){
			@Override
			public FileDownloadPath apply(String input) {
				return AutoBeanUtil.decode(FileDownloadPath.class, input);
			}			
		});
		
		this.dataProvider.getList().clear();
		this.dataProvider.getList().addAll(fileItems);
		this.dataProvider.refresh();
	}	
}
